// 引入express框架
const express = require('express');

// 引入数据库封装模块
const db = require('./db');

// 定时任务模块
const schedule = require('node-schedule');

// 引入request-promise用于网络请求
const rp = require('request-promise');

// 路径处理模块
const path = require('path');

// 创建web服务器
const app = express();

// 静态资源访问服务功能
app.use(express.static(path.join(__dirname,"public")));

// 接口调用凭证
let access;
// 获取凭证加锁
let accessLock = false;
// 提醒定时器序号
let planTList = {};

// 创建路由
// 签到
app.get('/signIn',function(req,res){
	let key = req.query.key;
	let openId = req.query.openId;
	let time = getTime(key,openId);
	
	// 时间戳误差在60秒内，密钥验证成功
	if(new Date().getTime()-time<60000){
		// 当前时间戳
		var nowTime = new Date().getTime();
		// 日期格式转为yyyy-mm-dd
		var date = timeToDate(nowTime);
		// 获取用户签到信息
		getSignInStatus(openId,(err,data)=>{
			if(err){
				res.send({
					code: 0,
					msg: err
				});
			}else{
				if(data.today){
					res.send({
						code: 1,
						msg: '今天已经签到过了！',
						data
					});
				}else{
					// 插入新的签到数据
					db.query('INSERT INTO sign_in(openId,date,time) VALUES(?,?,?)',[openId,date,nowTime],(err,results,fields)=>{
						if(err){
							res.send({
								code: 0,
								msg: err
							});
						}else{
							var add = 20+ (+data.lxDay+1)*5;
							var score = +data.score+add;
							// 更新签到分数
							db.query('UPDATE user_data SET score=? WHERE openId=?',[score,openId],(err,results,fields)=>{
								if(err){
									res.send({
										code: 0,
										msg: err
									});
								}else{
									// 全部签到日期
									data.allDate.unshift(date);
									// 今天是否签到
									data.today = true;
									// 连续签到天数
									data.lxDay += 1;
									// 签到分数
									data.score = score;
									res.send({
										code: 1,
										msg: '签到成功，积分+'+add,
										data
									});
								}
							});
						}
					});
				}
			}
		});
	}else{
		res.send({
			code: 0,
			msg: 'Invalid Key'
		});
	}
});

//补签
app.get('/reSign',function(req,res){
	let key = req.query.key;
	let openId = req.query.openId;
	let time = getTime(key,openId);
	// 时间戳误差在60秒内，密钥验证成功
	if(new Date().getTime()-time<60000){
		// 当前时间戳
		var nowTime = new Date().getTime();
		// 日期格式转为yyyy-mm-dd
		var date = timeToDate(nowTime);
		// 获取用户签到信息
		getSignInStatus(openId,(err,data)=>{
			if(err){
				res.send({
					code: 0,
					msg: err
				});
			}else{
				var len = data.forgetDate.length;
				if(len==0){
					res.send({
						code: 1,
						msg: '当前没有可补签的日期！',
						data
					});
				}else{
					if(data.isReSign){
						res.send({
							code: 1,
							msg: '今天已经补签过了！',
							data
						});
					}else{
						// 插入新的签到数据(最早的漏签日期)
						db.query('INSERT INTO sign_in(openId,date,time) VALUES(?,?,?)',[openId,data.forgetDate[len-1],nowTime],(err,results,fields)=>{
							if(err){
								res.send({
									code: 0,
									msg: err
								});
							}else{
								// 更新上一次签到日期
								db.query('UPDATE user_data SET prevReSign=? WHERE openId=?',[date,openId],(err,results,fields)=>{
									if(err){
										res.send({
											code: 0,
											msg: err
										});
									}else{
										// 重新获取用户签到信息
										getSignInStatus(openId,(err,data)=>{
											if(err){
												res.send({
													code: 0,
													msg: err
												});
											}else{
												res.send({
													code: 1,
													msg: '补签成功！',
													data
												});
											}
										});
									}
								});
							}
						});
					}
				}
			}
		});
	}else{
		res.send({
			code: 0,
			msg: 'Invalid Key'
		});
	}
});

// 查询签到记录
app.get('/getSignInStatus',function(req,res){
	let key = req.query.key;
	let openId = req.query.openId;
	let time = getTime(key,openId);
	// 时间戳误差在60秒内，密钥验证成功
	if(new Date().getTime()-time<60000){
		// 获取用户签到信息
		getSignInStatus(openId,(err,data)=>{
			if(err){
				res.send({
					code: 0,
					msg: err
				});
			}else{
				res.send({
					code: 1,
					msg: 'success',
					data
				});
			}
		});
	}else{
		res.send({
			code: 0,
			msg: 'Invalid Key'
		});
	}
});

// 反馈
app.get('/feedback',function(req,res){
	let key = req.query.key;
	let openId = req.query.openId;
	let title = req.query.title;
	let content = req.query.content;
	let urlList = JSON.stringify(req.query.urlList);
	let time = getTime(key,openId);
	// 时间戳误差在60秒内，密钥验证成功
	if(new Date().getTime()-time<60000){
		// 当前时间戳
		var nowTime = new Date().getTime();
		// 插入新的签到数据
		db.query('INSERT INTO feedback(openId,title,content,imgUrl,time) VALUES(?,?,?,?,?)',[openId,title,content,urlList,nowTime],(err,results,fields)=>{
			if(err){
				res.send({
					code: 0,
					msg: err
				});
			}else{
				res.send({
					code: 1,
					msg: '反馈成功！'
				});
			}
		});
	}else{
		res.send({
			code: 0,
			msg: 'Invalid Key'
		});
	}
});

// 增加订阅次数
app.get('/addPlanTime',function(req,res){
	let key = req.query.key;
	let openId = req.query.openId;
	let time = getTime(key,openId);
	// 时间戳误差在60秒内，密钥验证成功
	if(new Date().getTime()-time<60000){
		// 查询用户数据（积分、订阅次数、上一次签到日期）
		getUserData(openId,(err,userData)=>{
			if(err){
				res.send({
					code: 0,
					msg: err
				});
			}else{
				var planTime = parseInt(userData.planTime)+1;
				// 更新订阅次数
				db.query('UPDATE user_data SET planTime=? WHERE openId=?',[planTime,openId],(err,results,fields)=>{
					if(err){
						res.send({
							code: 0,
							msg: err
						});
					}else{
						res.send({
							code: 1,
							msg: '增加订阅次数成功！',
							planTime
						});
					}
				});
			}
		});
	}else{
		res.send({
			code: 0,
			msg: 'Invalid Key'
		});
	}
});

// 获取用户行程提醒信息
app.get('/getUserPlan',function(req,res){
	let key = req.query.key;
	let openId = req.query.openId;
	let time = getTime(key,openId);
	// 时间戳误差在60秒内，密钥验证成功
	if(new Date().getTime()-time<60000){
		// 获取用户签到信息
		getUserPlan(openId,(err,data)=>{
			if(err){
				res.send({
					code: 0,
					msg: err
				});
			}else{
				res.send({
					code: 1,
					msg: 'success',
					data
				});
			}
		});
	}else{
		res.send({
			code: 0,
			msg: 'Invalid Key'
		});
	}
});

// 新建行程提醒
app.get('/newPlan',function(req,res){
	let key = req.query.key;
	let openId = req.query.openId;
	let title = req.query.title;
	let hour = req.query.hour;
	let minute = req.query.minute;
	let dayOfWeek = '';
	let year = '';
	let month = '';
	let dayOfMonth = '';
	if(req.query.dayOfWeek){
		dayOfWeek = req.query.dayOfWeek;
	}else if(req.query.year&&req.query.month&&req.query.dayOfMonth){
		year = req.query.year;
		month = req.query.month;
		dayOfMonth = req.query.dayOfMonth;
	}
	let time = getTime(key,openId);
	// 时间戳误差在60秒内，密钥验证成功
	if(new Date().getTime()-time<60000){
		// 当前时间戳
		var nowTime = new Date().getTime();
		var planId = openId+nowTime;
		// 插入新的行程信息
		db.query('INSERT INTO plan(openId,title,hour,minute,dayOfWeek,year,month,dayOfMonth,planId) VALUES(?,?,?,?,?,?,?,?,?)',[openId,title,hour,minute,dayOfWeek,year,month,dayOfMonth,planId],(err,results,fields)=>{
			if(err){
				res.send({
					code: 0,
					msg: err
				});
			}else{
				// 获取用户行程提醒信息
				getUserPlan(openId,(err,data)=>{
					if(err){
						res.send({
							code: 0,
							msg: err
						});
					}else{
						// 设置定时发送任务
						setPlanTimer({
							openId,
							title,
							hour,
							minute,
							dayOfWeek,
							year,
							month,
							dayOfMonth,
							planId
						});
						// 查询用户数据（积分、订阅次数、上一次签到日期）
						getUserData(openId,(err,userData)=>{
							if(err){
								res.send({
									code: 0,
									msg: err
								});
							}else{
								var planTime = parseInt(userData.planTime)+1;
								// 更新订阅次数
								db.query('UPDATE user_data SET planTime=? WHERE openId=?',[planTime,openId],(err,results,fields)=>{
									if(err){
										res.send({
											code: 0,
											msg: err
										});
									}else{
										res.send({
											code: 1,
											msg: '新建行程提醒成功！',
											data,
											planTime
										});
									}
								});
							}
						});
					}
				});
			}
		});
	}else{
		res.send({
			code: 0,
			msg: 'Invalid Key'
		});
	}
});

// 删除行程提醒
app.get('/deletePlan',function(req,res){
	let key = req.query.key;
	let openId = req.query.openId;
	let planId = req.query.planId;
	let time = getTime(key,openId);
	// 时间戳误差在60秒内，密钥验证成功
	if(new Date().getTime()-time<60000){
		// 删除行程记录
		db.query('DELETE FROM plan WHERE planId=?',[planId],(err,results,fields)=>{
			if(err){
				res.send({
					code: 0,
					msg: err
				});
			}else{
				// 获取用户行程提醒信息
				getUserPlan(openId,(err,data)=>{
					if(err){
						res.send({
							code: 0,
							msg: err
						});
					}else{
						if(planTList[planId]){
							// 清除定时器
							planTList[planId].cancel();
							delete planTList[planId];
							console.log('————————————\nTime: ',new Date().toLocaleString(),'\ndeletePlanId: ',planId,'\nplanTList: ',Object.keys(planTList));
						}
						res.send({
							code: 1,
							msg: '删除行程提醒成功！',
							data
						});
					}
				});
			}
		});
	}else{
		res.send({
			code: 0,
			msg: 'Invalid Key'
		});
	}
});

// 监听端口
app.listen(3000);
console.log('————————————\n————————————\nTime: ',new Date().toLocaleString());
console.log(path.join(__dirname,"public"));
console.log('服務器启动成功');

// 定时刷新接口调用凭证
function access_timer(){
	// 初始化access
	updateAccess();
	// 每小时的1分1秒时获取一次access
    schedule.scheduleJob('1 1 * * * *', function(){
        updateAccess();
    }); 
}
// 开始获取接口调用凭证
access_timer();

// 获取接口调用凭证
function updateAccess(){
	if(accessLock) return;
	// 加锁
	accessLock = true;
	// 请求配置
	let options = {
		method: 'GET',
		url: 'https://api.weixin.qq.com/cgi-bin/token',
		qs: {
		  grant_type: 'client_credential',
		  appid: 'wx9bdb8358018926dc',
		  secret: 'fde0a1b7836084d1cc7aa271c0f36141'
		},
		json: true
	}

	rp(options)
		.then(res=>{
			if(res.access_token){
				access = res.access_token;
				console.log('————————————\nTime: ',new Date().toLocaleString(),'\nAccess_token: ',access);
				// 5秒后解锁
				setTimeout(()=>{
					accessLock = false;
				},5000);
			}else{
				// 失败时重新获取
				updateAccess();
			}
			
		})
		.catch(err=>{
			console.error(err);
			// 解锁
			accessLock = false;
		});
}

// 初始化行程计划
function initPlan(){
	// 查询用户签到日期
	db.query('SELECT * FROM plan',[],(err,results,fields)=>{
		if(err){
			initPlan();
			return;
		}else{
			for(let t of results){
	            setPlanTimer(t);
        	}
		}
	});
}
initPlan();

// 设置定时发送任务
function setPlanTimer(t){
	if(t.dayOfWeek){// 每周
    	var rule = new schedule.RecurrenceRule();
	    rule.dayOfWeek = parseInt(t.dayOfWeek)==7?0:parseInt(t.dayOfWeek);
	    rule.hour = parseInt(t.hour);
	    rule.minute = parseInt(t.minute);
	    rule.second = '1';
	    
	    planTList[t.planId] = schedule.scheduleJob(rule, function(){
	    	var time = timeToDate(new Date().getTime())+' '+t.hour+':'+t.minute;
	    	// 发送行程提醒(不删除行程)
	    	sendPlan(t.openId,t.title,time,t.planId);
	    });
	    console.log('————————————\nTime: ',new Date().toLocaleString(),'\naddPlanId: ',t.planId,'\nplanTList: ',Object.keys(planTList));
    }else if(t.year&&t.month&&t.dayOfMonth){// 定期
    	// yyyy-mm-dd HH:MM
    	var timeStr = t.year+'-'+t.month+'-'+t.dayOfMonth+' '+t.hour+':'+t.minute;
    	var date = new Date(timeStr);
    	
    	// 小于一分钟的通知直接发送
    	if(date.getTime()-new Date().getTime()<60000){
    		// 发送行程提醒(删除行程)
    		sendPlan(t.openId,t.title,timeStr,t.planId,true);
    	}else{// 定期发送
		    planTList[t.planId] = schedule.scheduleJob(date, function(){
		   		// 发送行程提醒(删除行程)
	    		sendPlan(t.openId,t.title,timeStr,t.planId,true);
	    	});
	    	console.log('————————————\nTime: ',new Date().toLocaleString(),'\naddPlanId: ',t.planId,'\nplanTList: ',Object.keys(planTList));
    	}
    }else{// 每天
    	var rule = new schedule.RecurrenceRule();
	    rule.hour = parseInt(t.hour);
	    rule.minute = parseInt(t.minute);
	    rule.second = '1';
	    
	    planTList[t.planId] = schedule.scheduleJob(rule, function(){
	    	var time = timeToDate(new Date().getTime())+' '+t.hour+':'+t.minute;
	    	// 发送行程提醒(不删除行程)
	    	sendPlan(t.openId,t.title,time,t.planId);
	    });
	    console.log('————————————\nTime: ',new Date().toLocaleString(),'\naddPlanId: ',t.planId,'\nplanTList: ',Object.keys(planTList));
    }
}

// 发送行程提醒
function sendPlan(openId,title,time,planId,beDelete){
	// 若凭证未获取到则5秒后再发送
	if(!access){
		setTimeout(()=>{sendPlan(openId,title,time,planId,beDelete)},5000);
		return;
	}
	// 查询用户数据（积分、订阅次数、上一次签到日期）
	getUserData(openId,(err,userData)=>{
		if(err){
			res.send({
				code: 0,
				msg: err
			});
		}else{
			var planTime = parseInt(userData.planTime);
			// 请求配置
			let options = {
				method: 'POST',
				url: 'https://api.weixin.qq.com/cgi-bin/message/subscribe/send',
				qs:{
					access_token: access
				},
				body: {
				  touser: openId,
				  template_id: 'Uh2sp9Y4qU_EW9fnywG7mheCJ3MBJU1ZJj3m_N6s9hU',
				  page: 'pages/mine/mine',
				  data: {
				  	'thing5': {
				  		'value': planTime<=1?(title.substr(0,9)+'【订阅次数为0请续订】'):title
				  	},
				  	'date4': {
				  		'value': time
				  	}
				  }
				},
				json: true
			}
			
			if(beDelete){
				// 删除已发送的行程记录
				db.query('DELETE FROM plan WHERE planId=?',[planId],(err,results,fields)=>{
					if(err){
						console.log(err)
					}
				});
				if(planTList[planId]){
					// 清除定时器
					planTList[planId].cancel();
					delete planTList[planId];
					console.log('————————————\nTime: ',new Date().toLocaleString(),'\ndeletePlanId: ',planId,'\nplanTList: ',Object.keys(planTList));
				}
			}
						
			rp(options)
				.then(res=>{
					console.log('————————————\nTime: ',new Date().toLocaleString(),'\nsendPlanId: ',planId,'\nsendRes: ',res);
					// 凭证过期或无效
					if(res.errcode==42001||res.errcode==40001){
						// 更新access
						updateAccess();
						// 5秒后重新发送
						setTimeout(()=>{sendPlan(openId,title,time,planId)},5000);
					}else if(res.errcode==43101){// 拒绝订阅或订阅次数为零
						// 更新订阅次数
						db.query('UPDATE user_data SET planTime=? WHERE openId=?',[0,openId],(err,results,fields)=>{
							if(err){
								console.log(err)
							}
						});
					}else{
						// 更新订阅次数
						db.query('UPDATE user_data SET planTime=? WHERE openId=?',[Math.max(planTime-1,0),openId],(err,results,fields)=>{
							if(err){
								console.log(err)
							}
						});
					}
				})
				.catch(console.log);
		}
	});
}

// 根据密钥和openId解析时间戳
function getTime(key,openId){
	if(typeof key!='string'||typeof openId!='string'||key.length>openId.length) return 0;
	let time = openId.split('').reverse().join('').substr(0,key.length).split('').map((a,i)=>key[i].charCodeAt()-a.charCodeAt()).join('');
	return time;
}
// 时间戳转yyyy-mm-dd格式
function timeToDate(time){
	var year = (new Date(+time).getFullYear()).toString();
	var month = (new Date(+time).getMonth()+1).toString();
	var day = (new Date(+time).getDate()).toString();
	// yyyy-mm-dd
	return year+'-'+(month.length==1?'0'+month:month)+'-'+(day.length==1?'0'+day:day);
}
// 获取用户行程提醒信息
function getUserPlan(openId,callback){
	// 查询用户签到日期
	db.query('SELECT * FROM plan WHERE openId=?',[openId],(err,results,fields)=>{
		if(err){
			callback(err);
		}else{
			// 所有行程信息
			let planList = [];
			for(var t of results){
	            var plan = {
	            	id: t.id,
	            	title: t.title,
	            	time: '每天 '+t.hour+':'+t.minute,
	            	planId: t.planId
	            };
	            if(t.dayOfWeek){
	            	plan['time'] = ['每天','每周一','每周二','每周三','每周四','每周五','每周六','每周日'][t.dayOfWeek]+' '+t.hour+':'+t.minute;
	            }else if(t.year&&t.month&&t.dayOfMonth){
	            	plan['time'] = t.year+'-'+t.month+'-'+t.dayOfMonth+' '+t.hour+':'+t.minute;
	            }
	            planList.push(plan);
        	}
			callback('',planList);
		}
	});
}
// 获取用户签到信息
function getSignInStatus(openId,callback){
	// 查询用户签到日期
	db.query('SELECT * FROM sign_in WHERE openId=?',[openId],(err,results,fields)=>{
		if(err){
			callback(err);
		}else{
			// 所有签到日期
			let allDate = [];
			for(var t of results) allDate.push(t.date);
			// 去重
			allDate = Array.from(new Set(allDate));
			// 日期从大到小排序
			allDate.sort((a,b)=>b.split('-')[0]-a.split('-')[0]||b.split('-')[1]-a.split('-')[1]||b.split('-')[2]-a.split('-')[2]);
			
			// 当前日期
			var nowDate = timeToDate(new Date().getTime());
			// 今天是否签到
			let today = allDate.includes(nowDate);
			
			// 连续签到天数
			let lxDay = 0;
			// 最后一天签到是今天或昨天
			if(allDate[0]&&(new Date(nowDate).getTime()-new Date(allDate[0]).getTime()<=24*3600*1000)){
				lxDay++;
				for(var i=1;i<allDate.length;i++){
					// 最后一天签到-i天
					var indexDate = timeToDate(new Date(new Date(allDate[0]).getTime()-i*24*3600*1000).getTime());
					
					if(indexDate==allDate[i]){
						lxDay++;
					}else{
						break;
					}
				}
			}
			
			// 七天内漏签日期
    		let forgetDate = [];
			for(var i=1;i<7;i++){
				// 当前日期-i天
				var indexDate = timeToDate(new Date(new Date(nowDate).getTime()-i*24*3600*1000).getTime());
				if(!allDate.includes(indexDate)) forgetDate.push(indexDate);
			}
			
			// 查询用户数据（积分、订阅次数、上一次签到日期）
			getUserData(openId,(err,userData)=>{
				if(err){
					callback(err);
				}else{
					// 今天是否补签
					var isReSign = (nowDate==userData.prevReSign);
					callback('',{
						allDate,
						today,
						lxDay,
						forgetDate,
						score: userData.score,
						planTime: userData.planTime,
						isReSign
					});
				}
			});
		}
	});
}
// 获取用户数据
function getUserData(openId,callback){
	// 查询用户数据（积分、订阅次数、上一次签到日期）
	db.query('SELECT * FROM user_data WHERE openId=?',[openId],(err,results,fields)=>{
		if(err){
			callback(err);
		}else{
			if(results.length>0){
				// 有用户记录
				callback('',{
					score: results[0].score,
					planTime: results[0].planTime,
					prevReSign: results[0].prevReSign
				});
			}else{
				// 新用户
				// 插入初始化用户数据
				db.query('INSERT INTO user_data(openId,score,planTime) VALUES(?,?,?)',[openId,0,0],(err,results,fields)=>{
					if(err){
						callback(err);
					}else{
						callback('',{
							score: 0,
							planTime: 0,
							prevReSign: null
						});
					}
				});
			}
		}
	});
}